package initialization;

import fundamental.*;

import java.util.*;

import static fundamental.Params.*;
import static localSearch.LocalSearch.*;

public class InitialFunction {
    /**
     * 随机选择一定数量的网点开放，并按照就近原则分配客户，再按照贪心原则规划路径
     */
    public static void H2getIniSol() {
        ArrayList<Cand> cands = selectCand();
        //System.out.println(cands.size());
        HashMap<Cand, HashSet<Customer>> candContainMap = new HashMap<>();
        for (Cand value : cands) {
            candContainMap.put(value, new HashSet<>());
        }
        //  对所有客户，按照距离最近的原则分配到相应的cand中
        for (Customer cust: custList
             ) {
            double minDist = Integer.MAX_VALUE;
            Cand minCand = null;
            for (Cand cand: candContainMap.keySet()
            ) {
                if (dist[cust.num][cand.num] < minDist) {
                    minDist = dist[cust.num][cand.num];
                    minCand = cand;
                }
            }
            candContainMap.get(minCand).add(cust);
        }
        //  对每一个cand，如果它覆盖有cust，则加入开放集合，并按照贪心原则规划路径
        for (Cand cand: candContainMap.keySet()
             ) {
            HashSet<Customer> waitCustSet = candContainMap.get(cand);
            if (!waitCustSet.isEmpty()) {
                openedCandSet.add(cand);
                //  按照就近原则形成第二级路径
                PriorityQueue<Customer> custToCandQueue = new PriorityQueue<>(Comparator.comparingDouble(o -> dist[o.num][cand.num]));
                custToCandQueue.addAll(waitCustSet);
                HashSet<Customer> needRemove = new HashSet<>();
                while (!custToCandQueue.isEmpty()) {
                    SecondVehicle v = new SecondVehicle(cap2,vFix2,vvar2);
                    v.add(cand);
                    v.start_end = cand;
                    secondVehicleList.add(v);
                    Customer nowCust = custToCandQueue.poll();
                    waitCustSet.remove(nowCust);
                    v.add(nowCust);
                    cand.loading += nowCust.demand+nowCust.overDemand;
                    while (nowCust.sameCand != cand && !waitCustSet.isEmpty()) {
                        Customer nextCust = getNearest(nowCust, waitCustSet);
                        if (v.cap - v.loading >= nextCust.demand) {
                            v.add(nextCust);
                            needRemove.add(nextCust);
                            waitCustSet.remove(nextCust);
                            cand.loading += nextCust.demand+nextCust.overDemand;
                            nowCust = nextCust;
                        }else {
                            break;
                        }
                    }
                    v.add(cand);
                    delayRemove2(needRemove, custToCandQueue);
                }
            }
        }
        //  构造一级路径（在这之前要对cand的loading进行处理），方式同构造一级路径，但是要注意路径长度
        for (Cand cand: openedCandSet
             ) {
            cand.flashLoading(cap1);
        }
        HashMap<Depot, HashSet<Cand>> depotCandMap = new HashMap<>();
        for (Depot depot: depotList
             ) {
            depotCandMap.put(depot, new HashSet<>());
        }
        for (Cand cand: openedCandSet
             ) {
            double minDist = Integer.MAX_VALUE;
            Depot minDepot = null;
            for (Depot depot: depotList
                 ) {
                //  这里没必要考虑不生效的5公里
                if (dist[depot.num][cand.num] < minDist) {
                    minDist = dist[depot.num][cand.num];
                    minDepot = depot;
                }
            }
            depotCandMap.get(minDepot).add(cand);
        }
        for (Depot depot: depotCandMap.keySet()
             ) {
            HashSet<Cand> waitCandSet = depotCandMap.get(depot);
            PriorityQueue<Cand> candToDepotQueue = new PriorityQueue<>(Comparator.comparingDouble(o -> dist[o.num][depot.num]));
            candToDepotQueue.addAll(waitCandSet);
            HashSet<Cand> needRemove = new HashSet<>();
            while (!candToDepotQueue.isEmpty()) {
                FirstVehicle v = new FirstVehicle(cap1,vFix1,vvar1);
                v.add(depot);
                v.start_end = depot;
                firstVehicleList.add(v);
                Cand nowCand = candToDepotQueue.poll();
                waitCandSet.remove(nowCand);
                v.add(nowCand);
                while (!waitCandSet.isEmpty() && v.route.size() < 3) {
                    Cand nextCand = getNearest(nowCand, waitCandSet);
                    if (v.cap - v.loading >= nextCand.loading) {
                        v.add(nextCand);
                        needRemove.add(nextCand);
                        waitCandSet.remove(nextCand);
                        nowCand = nextCand;
                    }else break;
                }
                v.add(depot);
                delayRemove1(needRemove, candToDepotQueue);
            }
        }
        secCost = getSecCost();
        firCost = getFirCost();
        //Check.check1();
    }

    /**
     * 按照各cand （备选点/行驶成本） 距离范围内的点的数量从高到低选择cand
     * @return
     */
    public static ArrayList<Cand> selectCand() {
        //  计算各candx公里内涵盖的点
        ArrayList<Cand> sol = new ArrayList<>();
        HashMap<Cand, HashSet<Customer>> candCoverMap = new HashMap<>();
        double thres = clusterThres;
        for (Cand cand: candSet
             ) {
            HashSet<Customer> set = new HashSet<>();
            for (Customer cust: custSet
                 ) {
                if (dist[cand.num][cust.num] <= thres) {
                    set.add(cust);
                }
            }
            candCoverMap.put(cand, set);
        }
        int count = 0;
        while (count < custNum) {
            int maxNum = -1;
            Cand maxCand = null;
            for (Cand cand: candCoverMap.keySet()
                 ) {
                if (maxNum < candCoverMap.get(cand).size()) {
                    maxNum = candCoverMap.get(cand).size();
                    maxCand = cand;
                }
            }
            sol.add(maxCand);
            HashSet<Customer> removes = new HashSet<>(candCoverMap.get(maxCand));
            for (Cand cand: candCoverMap.keySet()
                 ) {
                HashSet<Customer> temp = candCoverMap.get(cand);
                temp.removeIf(removes::contains);
            }
            count += removes.size();
        }
        return sol;
    }

    /**
     * 获得目标集合中距离目标点距离最近的点
     * @param aimCust
     * @param aimSet
     * @return
     */
    public static Customer getNearest(Customer aimCust, HashSet<Customer> aimSet) {
        double minDist = Integer.MAX_VALUE;
        Customer minCust = null;
        for (Customer cust: aimSet
             ) {
            if (dist[cust.num][aimCust.num] < minDist) {
                minDist = dist[cust.num][aimCust.num];
                minCust = cust;
            }
        }
        return minCust;
    }

    /**
     * 获得目标集合中距离目标点距离最近的点
     * @param aimCand
     * @param aimSet
     * @return
     */
    public static Cand getNearest(Cand aimCand, HashSet<Cand> aimSet) {
        double minDist = Integer.MAX_VALUE;
        Cand minCand = null;
        for (Cand cand: aimSet
             ) {
            if (dist[cand.num][aimCand.num] < minDist) {
                minDist = dist[cand.num][aimCand.num];
                minCand = cand;
            }
        }
        return minCand;
    }

    private static void delayRemove2(HashSet<Customer> needRemove, PriorityQueue<Customer> custToCandQueue) {
        while (!custToCandQueue.isEmpty()) {
            Customer cust = custToCandQueue.peek();
            if (needRemove.contains(cust)) {
                needRemove.remove(cust);
                custToCandQueue.poll();
            }else break;
        }
    }

    public static void delayRemove1(HashSet<Cand> needRemove, PriorityQueue<Cand> candToDepotQueue) {
        while (!candToDepotQueue.isEmpty()) {
            Cand cand = candToDepotQueue.peek();
            if (needRemove.contains(cand)) {
                needRemove.remove(cand);
                candToDepotQueue.poll();
            }else break;
        }
    }

    /**
     * 拉通计算成本
     * @param firV
     * @param secV
     * @return
     */
    public static double getCost(ArrayList<FirstVehicle> firV, ArrayList<SecondVehicle> secV) {
        double cost = 0;
        //  第一级路径的成本（车辆的固定成本+行驶成本）
        for (Vehicle v: firV
             ) {
            cost += v.fcost;
            for (int i = 0; i < v.route.size()-1; i++) {
                Point p1 = v.route.get(i);
                Point p2 = v.route.get(i+1);
                cost += v.vcost * Math.max(dist[p1.num][p2.num]-5, 0);
                if (p1 instanceof Cand) {
                    Cand c = (Cand) p1;
                    Depot d = (Depot) v.route.get(0);
                    cost += (double) c.overLoading/v.cap * v.fcost;  //  对于每一个超量的cand，需要单独运输，计算车辆固定成本
                    cost += (double) c.overLoading/v.cap * 2 * Math.max(dist[d.num][c.num]-5,0) * v.vcost;  //  对于每一个超量的cand，需要单独运输，计算车辆行驶成本
                }
            }
        }
        //  第二级路径的成本（车辆的固定成本+行驶成本）
        for (Vehicle v: secV
             ) {
            cost += v.fcost;
            for (int i = 0; i < v.route.size()-1; i++) {
                Point p1 = v.route.get(i);
                Point p2 = v.route.get(i+1);
                cost += v.vcost*dist[p1.num][p2.num];
                if (p1 instanceof Customer) {
                    Customer cust = (Customer) p1;
                    Cand cand = (Cand) v.route.get(0);
                    cost += (double) cust.overDemand/v.cap * v.fcost;
                    cost += (double) cust.overDemand/v.cap * 2 * dist[cand.num][cust.num] * v.vcost;
                }
            }
        }
        //  计算网点开放成本
        for (Cand cand: openedCandSet
             ) {
            cost += cand.cost;
        }
        return cost;
    }
}
